home *** CD-ROM | disk | FTP | other *** search
- // BEGIN FLOCK GPL
- //
- // Copyright Flock Inc. 2005-2007
- // http://flock.com
- //
- // This file may be used under the terms of of the
- // GNU General Public License Version 2 or later (the "GPL"),
- // http://www.gnu.org/licenses/gpl.html
- //
- // Software distributed under the License is distributed on an "AS IS" basis,
- // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- // for the specific language governing rights and limitations under the
- // License.
- //
- // END FLOCK GPL
-
- const PS_CONTRACTID = '@flock.com/poller-service;1';
- const PS_CLASSID = Components.ID('{a2b4cd0e-804b-4578-8c3c-4b61f8c1047a}');
- const PS_CLASSNAME = 'Flock Poller';
-
-
- const PREF_POLLER_BRANCH = 'flock.poller'
- const PREF_REFRESH_DELAY = '.refreshDelay';
- const PREF_REFRESH_TIMEOUT = '.refreshTimeout';
- const PREF_DEFAULT_REFRESH_INTERVAL = '.defaultRefreshInterval';
- const PREF_MAX_CONCURRENT_REFRESHES = '.maxConcurrentRefreshes';
-
- // According to Mozilla docs, timers can't be set for more than approximately
- // 6 hours. We'll limit to 4 hours just to be conservative.
- const MAX_TIMER_INTERVAL = 4 * 60 * 60 * 1000;
-
- // Delay actual polling for 30 seconds after component initialization, so
- // there is reduced contention among other browser startup operations
- const STARTUP_DELAY = 30 * 1000;
-
-
- const Ci = Components.interfaces;
- const Cc = Components.classes;
- const Cr = Components.results;
-
-
- function getObserverService() {
- return Cc['@mozilla.org/observer-service;1']
- .getService(Ci.nsIObserverService);
- }
-
-
- function RefreshListener(poller, urn, serviceId) {
- this._poller = poller;
- this._urn = urn;
- this._serviceId = serviceId;
- }
-
- RefreshListener.prototype = {
- onResult: function PSL_onResult() {
- this._poller._logger.info('Successful refresh for ' + this._urn);
- this._poller._finishedRefresh(this._urn);
- },
- onError: function PSL_onError(error) {
- this._poller._logger.error('The service ' + this._serviceId + ' returned an ' +
- 'error while refreshing ' + this._urn);
- this._poller._finishedRefresh(this._urn);
- },
- notify: function PSL_notify(timer) {
- this._poller._logger.error('The service ' + this._serviceId + ' timed out ' +
- 'while refreshing ' + this._urn);
- this._poller._finishedRefresh(this._urn);
- },
- QueryInterface: function PSL_QueryInterface(iid) {
- if (iid.equals(Ci.flockIPollerListener) ||
- iid.equals(Ci.nsITimerCallback) ||
- iid.equals(Ci.nsISupports))
- return this;
- throw Cr.NS_ERROR_NO_INTERFACE;
- }
- }
-
-
- function PollerService() {
- var obs = getObserverService();
- obs.addObserver(this, 'flock-data-ready', false);
- obs.addObserver(this, 'xpcom-shutdown', false);
- }
-
- PollerService.prototype = {
- _start: function PS__start() {
- this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
- this._logger.init('poller');
- this._logger.info('starting up...');
-
- this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);
- var evtID = this._profiler.profileEventStart('poller-start');
-
- this._timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
-
- this._coop = Cc["@flock.com/singleton;1"]
- .getService(Ci.flockISingleton)
- .getSingleton("chrome://flock/content/common/load-faves-coop.js")
- .wrappedJSObject;
-
- var prefService = Cc['@mozilla.org/preferences-service;1']
- .getService(Ci.nsIPrefBranch2);
- prefService.addObserver(PREF_POLLER_BRANCH, this, false);
-
- this.observe(null, 'nsPref:changed', PREF_REFRESH_DELAY);
- this.observe(null, 'nsPref:changed', PREF_REFRESH_TIMEOUT);
- this.observe(null, 'nsPref:changed', PREF_DEFAULT_REFRESH_INTERVAL);
- this.observe(null, 'nsPref:changed', PREF_MAX_CONCURRENT_REFRESHES);
-
- this._initializeQueue();
- this._watchRDFEvents();
-
- this._profiler.profileEventEnd(evtID, "");
- },
- _shutdown: function PS__shutdown() {
- var prefService = Cc['@mozilla.org/preferences-service;1']
- .getService(Ci.nsIPrefBranch2);
- prefService.removeObserver(PREF_POLLER_BRANCH, this);
-
- this._timer.cancel();
- this._timer = null;
- },
-
- _prefChanged: function PS__prefChanged(pref) {
- var prefService = Cc['@mozilla.org/preferences-service;1']
- .getService(Ci.nsIPrefService);
- var prefBranch = prefService.getBranch(PREF_POLLER_BRANCH);
-
- switch (pref) {
- case PREF_REFRESH_DELAY:
- this._refreshDelay = prefBranch.getIntPref(pref);
- break;
-
- case PREF_REFRESH_TIMEOUT:
- this._refreshTimeout = prefBranch.getIntPref(pref);
- break;
-
- case PREF_DEFAULT_REFRESH_INTERVAL:
- this._defaultRefreshInterval = prefBranch.getIntPref(pref);
- break;
-
- case PREF_MAX_CONCURRENT_REFRESHES:
- this._maxConcurrentRefreshes = prefBranch.getIntPref(pref);
- break;
- }
- },
-
- _watchRDFEvents: function PS__watchRDFEvents() {
- var RDFS = Cc['@mozilla.org/rdf/rdf-service;1']
- .getService(Ci.nsIRDFService);
- var nextRefresh = RDFS.GetResource('http://flock.com/rdf#nextRefresh');
- var isPollable = RDFS.GetResource('http://flock.com/rdf#isPollable');
-
- var faves = Cc['@mozilla.org/rdf/datasource;1?name=flock-favorites']
- .getService(Ci.flockIRDFObservable);
- faves.addArcObserver(Ci.flockIRDFObserver.TYPE_ALL, null, nextRefresh, null,
- this);
- faves.addArcObserver(Ci.flockIRDFObserver.TYPE_ALL, null, isPollable, null,
- this);
- },
- rdfChanged: function PS_rdfChanged(ds, type, rsrc, pred, obj, oldObj) {
- var pollable = this._coop.get_from_resource(rsrc);
-
- if (pollable && pollable.isPollable) {
- if (pollable.nextRefresh)
- this._addToQueue(pollable);
- else
- pollable.nextRefresh = new Date();
- } else {
- rsrc.QueryInterface(Ci.nsIRDFResource);
- this._removeFromQueue(rsrc.ValueUTF8);
- }
- },
-
- _initializeQueue: function PS__initializeQueue() {
- var queue = [];
- var dates = {};
-
- var RDFS = Cc['@mozilla.org/rdf/rdf-service;1']
- .getService(Ci.nsIRDFService);
- var isPollable = RDFS.GetResource('http://flock.com/rdf#isPollable');
-
- var faves = Cc['@mozilla.org/rdf/datasource;1?name=flock-favorites']
- .getService(Ci.nsIRDFDataSource);
-
- var pollables = faves.GetSources(isPollable, RDFS.GetLiteral('true'), true);
- while (pollables.hasMoreElements()) {
- var pollable = this._coop.get_from_resource(pollables.getNext());
-
- var urn = pollable.id();
- queue.push(urn);
-
- if (!pollable.nextRefresh)
- pollable.nextRefresh = new Date();
-
- dates[urn] = pollable.nextRefresh;
- }
-
- function sorter(a, b) {
- return dates[a] - dates[b];
- }
- queue.sort(sorter);
-
- this._queue = queue;
- this._dates = dates;
-
- this._refreshing = {};
- this._refreshOrder = [];
-
- this._lastRefresh = new Date(0);
-
- this._logger.debug('queue initialized: ' + this._queue.toSource());
-
- this._initTimer(STARTUP_DELAY);
- },
-
- _addToQueue: function PS__addToQueue(obj) {
- var urn = obj.id();
- var nextRefresh = obj.nextRefresh;
-
- var date = this._dates[urn];
- if (date) {
- if (date - nextRefresh == 0)
- return;
- else
- this._queue.splice(this._queue.indexOf(urn), 1);
- }
-
- this._dates[urn] = nextRefresh;
-
- for (var i = 0; i < this._queue.length; i++) {
- var date = this._dates[this._queue[i]];
- if (nextRefresh < date) {
- this._queue.splice(i, 0, urn);
- if (i == 0)
- this._recalculateTimer();
-
- this._logger.debug('queue updated: ' + this._queue.toSource());
- return;
- }
- }
-
- this._queue.push(urn);
- if (this._queue.length == 1)
- this._recalculateTimer();
-
- this._logger.debug('queue updated: ' + this._queue.toSource());
- },
-
- _removeFromQueue: function PS__removeFromQueue(urn) {
- var date = this._dates[urn];
- if (!date)
- return;
-
- delete this._dates[urn];
-
- var index = this._queue.indexOf(urn);
- if (index == 0) {
- this._queue.shift();
- this._recalculateTimer();
- } else {
- this._queue.splice(index, 1);
- }
-
- this._logger.debug('queue updated: ' + this._queue.toSource());
- },
-
- _initTimer: function PS__initTimer(interval) {
- this._logger.debug('next refresh: ' + interval / 1000);
- this._timer.initWithCallback(this, interval, Ci.nsITimer.TYPE_ONE_SHOT);
- },
- _recalculateTimer: function PS__recalculateTimer() {
- this._timer.cancel();
-
- if (this._queue.length) {
- var date = this._dates[this._queue[0]];
- var now = new Date();
-
- var interval = date > now ? date - now : 0;
- interval = Math.min(interval, MAX_TIMER_INTERVAL);
-
- if (this._refreshOrder.length) {
- var urn = this._refreshOrder[this._refreshOrder.length - 1];
- var start = this._refreshing[urn].start;
- var delay = (this._refreshDelay * 1000) - (now - start);
- interval = Math.max(interval, delay);
-
- if (this._refreshOrder.length > this._maxConcurrentRefreshes - 1) {
- urn = this._refreshOrder[0];
- start = this._refreshing[urn].start;
- delay = (this._refreshTimeout * 1000) - (now - start);
- interval = Math.max(interval, delay);
- }
- } else {
- var delay = (this._refreshDelay * 1000) - (now - this._lastRefresh);
- interval = Math.max(interval, delay);
- }
-
- this._initTimer(interval);
- }
- },
-
- _finishedRefresh: function PS__finishedRefresh(urn) {
- var index = this._refreshOrder.indexOf(urn);
- if (index == -1)
- return;
-
- var refreshInfo = this._refreshing[urn]
-
- if (refreshInfo.timer) {
- refreshInfo.timer.cancel();
- }
-
- delete this._refreshing[urn];
- this._refreshOrder.splice(index, 1);
-
- var obj = this._coop.get(urn);
- if (!obj)
- return;
-
- obj.isRefreshing = false;
-
- var now = new Date();
- if (obj.nextRefresh < now) {
- var refreshInterval = obj.refreshInterval ? obj.refreshInterval
- : this._defaultRefreshInterval;
-
- obj.nextRefresh = new Date(now.getTime() + refreshInterval * 1000);
- }
-
- this._recalculateTimer();
-
- var obs = getObserverService();
- obs.notifyObservers(null, "refresh-myworld", null);
- },
- _refresh: function PS__refresh(obj) {
- var urn = obj.id();
- if (this._refreshing[urn])
- return;
-
- var serviceId = obj.serviceId;
- this._logger.debug('serviceId = ' + serviceId);
-
- var service = null;
- if (serviceId && Cc[serviceId]) {
- try {
- service = Cc[serviceId].getService(Ci.flockIPollingService);
- }
- catch (e) {
- this._logger.error('Problem getting "' + serviceId + '" as a ' +
- 'flockIPollingService, while trying to refresh ' +
- urn);
- }
- }
-
- if (service) {
- this._logger.info('Refreshing ' + urn);
- try {
- var now = new Date();
-
- this._refreshing[urn] = { start: now };
- this._refreshOrder.push(urn);
-
- obj.isRefreshing = true;
-
- var listener = new RefreshListener(this, urn, serviceId);
- service.refresh(urn, listener);
-
- var refreshInfo = this._refreshing[urn];
- if (refreshInfo) {
- var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- refreshInfo.timer = timer;
- timer.initWithCallback(listener, this._refreshTimeout * 1000,
- Ci.nsITimer.TYPE_ONE_SHOT);
- }
-
- this._lastRefresh = now;
- }
- catch (e) {
- this._logger.error("Exception while "
- + serviceId
- + " was refreshing a stream: "
- + e);
- this._finishedRefresh(urn);
- }
- } else {
- this._logger.error('Problem getting the service, while trying to ' +
- 'refresh ' + urn);
- }
- },
-
- notify: function PS_notify(timer) {
- var urn = this._queue[0];
- var date = this._dates[urn];
-
- var now = new Date();
- if (now < date) {
- this._recalculateTimer();
- return;
- }
-
- this._queue.shift();
- delete this._dates[urn];
-
- var obj = this._coop.get(urn);
- if (obj)
- this._refresh(this._coop.get(urn));
- else
- this._logger.warn('trying to refresh nonexistent object: ' + urn);
-
- this._recalculateTimer();
- },
-
- observe: function PS_observe(subject, topic, state) {
- var obs = getObserverService();
-
- switch (topic) {
- case 'flock-data-ready':
- obs.removeObserver(this, 'flock-data-ready');
- this._start();
- break;
-
- case 'xpcom-shutdown':
- obs.removeObserver(this, 'xpcom-shutdown');
- this._shutdown();
- break;
-
- case 'nsPref:changed':
- this._prefChanged(state);
- break;
- }
- },
-
- forceRefresh: function PS_forceRefresh(urn) {
- var obj = this._coop.get(urn);
- if (!obj)
- throw 'URN ' + urn + ' does not exist';
-
- obj.nextRefresh = new Date(0);
- },
-
- getInterfaces: function PS_getInterfaces(countRef) {
- var interfaces = [Ci.flockIPollerService, Ci.flockIRDFObserver,
- Ci.nsITimerCallback, Ci.nsIObserver, Ci.nsIClassInfo,
- Ci.nsISupports];
- countRef.value = interfaces.length;
- return interfaces;
- },
- getHelperForLanguage: function PS_getHelperForLanguage(language) {
- return null;
- },
- contractID: PS_CONTRACTID,
- classDescription: PS_CLASSNAME,
- classID: PS_CLASSID,
- implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
- flags: Ci.nsIClassInfo.SINGLETON,
-
- QueryInterface: function PS_QueryInterface(iid) {
- if (iid.equals(Ci.flockIPollerService) ||
- iid.equals(Ci.flockIRDFObserver) ||
- iid.equals(Ci.nsITimerCallback) ||
- iid.equals(Ci.nsIObserver) ||
- iid.equals(Ci.nsIClassInfo) ||
- iid.equals(Ci.nsISupports))
- return this;
- throw Cr.NS_ERROR_NO_INTERFACE;
- }
- }
-
-
- function GenericComponentFactory(ctor) {
- this._ctor = ctor;
- }
-
- GenericComponentFactory.prototype = {
-
- _ctor: null,
-
- // nsIFactory
- createInstance: function(outer, iid) {
- if (outer != null)
- throw Cr.NS_ERROR_NO_AGGREGATION;
- return (new this._ctor()).QueryInterface(iid);
- },
-
- // nsISupports
- QueryInterface: function(iid) {
- if (iid.equals(Ci.nsIFactory) ||
- iid.equals(Ci.nsISupports))
- return this;
- throw Cr.NS_ERROR_NO_INTERFACE;
- },
- };
-
- var Module = {
- QueryInterface: function(iid) {
- if (iid.equals(Ci.nsIModule) ||
- iid.equals(Ci.nsISupports))
- return this;
-
- throw Cr.NS_ERROR_NO_INTERFACE;
- },
-
- getClassObject: function(cm, cid, iid) {
- if (!iid.equals(Ci.nsIFactory))
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-
- if (cid.equals(PS_CLASSID))
- return new GenericComponentFactory(PollerService);
-
- throw Cr.NS_ERROR_NO_INTERFACE;
- },
-
- registerSelf: function(cm, file, location, type) {
- var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
- cr.registerFactoryLocation(PS_CLASSID, PS_CLASSNAME, PS_CONTRACTID,
- file, location, type);
-
- var catman = Cc['@mozilla.org/categorymanager;1']
- .getService(Ci.nsICategoryManager);
- catman.addCategoryEntry('flock-startup', PS_CLASSNAME,
- 'service,' + PS_CONTRACTID,
- true, true);
- },
-
- unregisterSelf: function(cm, location, type) {
- var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
- cr.unregisterFactoryLocation(PS_CLASSID, location);
- },
-
- canUnload: function(cm) {
- return true;
- },
- };
-
- function NSGetModule(compMgr, fileSpec)
- {
- return Module;
- }
-